import React, { useMemo, useState } from 'react'
import { DateTime, Duration, Interval } from 'luxon'
import p from 'prop-types'
import FlatList from '../lists/FlatList'
import { gql, useQuery } from '@apollo/client'
import { relativeDate } from '../util/timeFormat'
import {
  Card,
  Grid,
  FormControlLabel,
  Switch,
  TextField,
  MenuItem,
  makeStyles,
} from '@material-ui/core'
import { UserAvatar } from '../util/avatars'
import FilterContainer from '../util/FilterContainer'
import { UserSelect } from '../selection'
import { useURLParam, useResetURLParams } from '../actions'
import { ScheduleTZFilter } from './ScheduleTZFilter'
import ScheduleNewOverrideFAB from './ScheduleNewOverrideFAB'
import ScheduleOverrideCreateDialog from './ScheduleOverrideCreateDialog'
import { ISODatePicker } from '../util/ISOPickers'

// query name is important, as it's used for refetching data after mutations
const query = gql`
  query scheduleShifts($id: ID!, $start: ISOTimestamp!, $end: ISOTimestamp!) {
    schedule(id: $id) {
      id
      shifts(start: $start, end: $end) {
        user {
          id
          name
        }
        start
        end
        truncated
      }
    }
  }
`

const durString = (dur) => {
  if (dur.months) {
    return `${dur.months} month${dur.months > 1 ? 's' : ''}`
  }
  if (dur.days % 7 === 0) {
    const weeks = dur.days / 7
    return `${weeks} week${weeks > 1 ? 's' : ''}`
  }
  return `${dur.days} day${dur.days > 1 ? 's' : ''}`
}

const useStyles = makeStyles({
  datePicker: {
    width: '100%',
  },
})

function ScheduleShiftList({ scheduleID }) {
  const classes = useStyles()

  const [create, setCreate] = useState(null)
  const [specifyDuration, setSpecifyDuration] = useState(false)
  const [isClear, setIsClear] = useState(false)

  const [duration, setDuration] = useURLParam('duration', 'P14D')
  const [zone] = useURLParam('tz', 'local')
  const [userFilter, setUserFilter] = useURLParam('userFilter', [])
  const [activeOnly, setActiveOnly] = useURLParam('activeOnly', false)

  const defaultStart = useMemo(
    () => DateTime.fromObject({ zone }).startOf('day').toISO(),
    [zone],
  )
  const [_start, setStart] = useURLParam('start', defaultStart)
  const start = useMemo(
    () => (activeOnly ? DateTime.utc().toISO() : _start),
    [activeOnly, _start],
  )

  const end = DateTime.fromISO(start, { zone })
    .plus(Duration.fromISO(duration))
    .toISO()

  const handleFilterReset = useResetURLParams(
    'userFilter',
    'start',
    'activeOnly',
    'tz',
    'duration',
  )

  const { data } = useQuery(query, {
    variables: {
      id: scheduleID,
      start,
      end,
    },
  })

  function items() {
    const _shifts =
      data?.schedule?.shifts?.map((s) => ({
        ...s,
        userID: s.user.id,
        userName: s.user.name,
      })) ?? []

    let shifts = _shifts
      .filter((s) => !userFilter.length || userFilter.includes(s.userID))
      .map((s) => ({
        ...s,
        start: DateTime.fromISO(s.start, { zone }),
        end: DateTime.fromISO(s.end, { zone }),
        interval: Interval.fromDateTimes(
          DateTime.fromISO(s.start, { zone }),
          DateTime.fromISO(s.end, { zone }),
        ),
      }))

    if (activeOnly) {
      const now = DateTime.fromObject({ zone })
      shifts = shifts.filter((s) => s.interval.contains(now))
    }

    if (!shifts.length) return []

    const displaySpan = Interval.fromDateTimes(
      DateTime.fromISO(start, { zone }).startOf('day'),
      DateTime.fromISO(end, { zone }).startOf('day'),
    )

    const result = []
    displaySpan.splitBy({ days: 1 }).forEach((day) => {
      const dayShifts = shifts.filter((s) => day.overlaps(s.interval))
      if (!dayShifts.length) return
      result.push({
        subHeader: relativeDate(day.start),
      })
      dayShifts.forEach((s) => {
        let shiftDetails = ''
        const startTime = s.start.toLocaleString({
          hour: 'numeric',
          minute: 'numeric',
        })
        const endTime = s.end.toLocaleString({
          hour: 'numeric',
          minute: 'numeric',
        })
        if (s.interval.engulfs(day)) {
          // shift (s.interval) spans all day
          shiftDetails = 'All day'
        } else if (day.engulfs(s.interval)) {
          // shift is inside the day
          shiftDetails = `From ${startTime} to ${endTime}`
        } else if (day.contains(s.end)) {
          shiftDetails = `Active until${
            s.truncated ? ' at least' : ''
          } ${endTime}`
        } else {
          // shift starts and continues on for the rest of the day
          shiftDetails = `Active after ${startTime}`
        }
        result.push({
          title: s.userName,
          subText: shiftDetails,
          icon: <UserAvatar userID={s.userID} />,
        })
      })
    })

    return result
  }

  function renderDurationSelector() {
    // Dropdown options (in ISO_8601 format)
    // https://en.wikipedia.org/wiki/ISO_8601#Durations
    const quickOptions = ['P1D', 'P3D', 'P7D', 'P14D', 'P1M']
    const clamp = (min, max, value) => Math.min(max, Math.max(min, value))

    if (quickOptions.includes(duration) && !specifyDuration) {
      return (
        <TextField
          select
          fullWidth
          label='Time Limit'
          disabled={activeOnly}
          value={duration}
          onChange={(e) => {
            e.target.value === 'SPECIFY'
              ? setSpecifyDuration(true)
              : setDuration(e.target.value)
          }}
        >
          {quickOptions.map((opt) => (
            <MenuItem value={opt} key={opt}>
              {durString(Duration.fromISO(opt))}
            </MenuItem>
          ))}
          <MenuItem value='SPECIFY'>Specify...</MenuItem>
        </TextField>
      )
    }
    return (
      <TextField
        fullWidth
        label='Time Limit (days)'
        value={isClear ? '' : Duration.fromISO(duration).as('days')}
        disabled={activeOnly}
        max={30}
        min={1}
        type='number'
        onBlur={() => setIsClear(false)}
        onChange={(e) => {
          setIsClear(e.target.value === '')
          if (Number.isNaN(parseInt(e.target.value, 10))) {
            return
          }
          setDuration(
            Duration.fromObject({
              days: clamp(1, 30, parseInt(e.target.value, 10)),
            }).toISO(),
          )
        }}
      />
    )
  }

  const dur = Duration.fromISO(duration)
  const timeStr = durString(dur)

  const zoneText = zone === 'local' ? 'local time' : zone
  const userText = userFilter.length ? ' for selected users' : ''
  const note = activeOnly
    ? `Showing currently active shifts${userText} in ${zoneText}.`
    : `Showing shifts${userText} up to ${timeStr} from ${DateTime.fromISO(
        start,
        {
          zone,
        },
      ).toLocaleString()} in ${zoneText}.`
  return (
    <React.Fragment>
      <ScheduleNewOverrideFAB onClick={(variant) => setCreate(variant)} />
      <Card style={{ width: '100%' }}>
        <FlatList
          headerNote={note}
          items={items()}
          headerAction={
            <FilterContainer
              onReset={() => {
                handleFilterReset()
                setSpecifyDuration(false)
              }}
            >
              <Grid item xs={12}>
                <FormControlLabel
                  control={
                    <Switch
                      checked={activeOnly}
                      onChange={(e) => setActiveOnly(e.target.checked)}
                      value='activeOnly'
                    />
                  }
                  label='Active shifts only'
                />
              </Grid>
              <Grid item xs={12}>
                <ScheduleTZFilter scheduleID={scheduleID} />
              </Grid>
              <Grid item xs={12}>
                <ISODatePicker
                  className={classes.datePicker}
                  disabled={activeOnly}
                  label='Start Date'
                  name='filterStart'
                  value={start}
                  onChange={(v) => setStart(v)}
                />
              </Grid>
              <Grid item xs={12}>
                {renderDurationSelector()}
              </Grid>
              <Grid item xs={12}>
                <UserSelect
                  label='Filter users...'
                  multiple
                  value={userFilter}
                  onChange={setUserFilter}
                />
              </Grid>
            </FilterContainer>
          }
        />
      </Card>
      {create && (
        <ScheduleOverrideCreateDialog
          scheduleID={scheduleID}
          variant={create}
          onClose={() => setCreate(null)}
        />
      )}
    </React.Fragment>
  )
}

ScheduleShiftList.propTypes = {
  scheduleID: p.string.isRequired,
}

export default ScheduleShiftList
